﻿using AJWPFAdmin.Core.Crypto.RSA;
using AJWPFAdmin.Core.ExtensionMethods;
using AJWPFAdmin.Core.Models;
using AJWPFAdmin.Core.Utils;
using AJWPFAdmin.Services.EF;
using FFmpeg.AutoGen;
using Masuit.Tools.Security;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Identity.Client;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Prism.Mvvm;
using Quartz.Impl.AdoJobStore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace AJWPFAdmin.Services
{
    public class AJConfigService : IDisposable
    {
        public IConfigurationRoot Config { get; set; }

        private HttpClient _client;

        private static readonly string APIDOMAIN = "http://mqttpc.tccxtwas.com";

        public int PID { get; set; }

        private string _machineMd5;
        public string MACHINEMD5
        {
            get => _machineMd5;
            private set => _machineMd5 = value;
        }

        private bool _isAuthorized;
        public bool IsAuthorized
        {
            get => _isAuthorized;
            set => _isAuthorized = value;
        }

        public TrialConfig TrialConfigInfo { get; set; }

        public AJConfigService()
        {
            Config = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).Build();

            _client = new HttpClient() { Timeout = TimeSpan.FromSeconds(5) };

            _machineMd5 = CommonUtil.MachineMD5();
        }

        public async Task<ProcessResult> TryActiveAsync(string registerCode = null, bool tryLocally = true, bool autoMode = false)
        {
            using var db = DbService.GetDbContext(this);

            var cfgs = db.SystemConfigDictionaries.Where(p => p.Key == Core.Enums.SystemConfigKey.LicenseConfig
            || p.Key == Core.Enums.SystemConfigKey.TrialConfig).ToList();

            var trialCfg = cfgs.FirstOrDefault(p => p.Key == Core.Enums.SystemConfigKey.TrialConfig);

            if (autoMode && trialCfg != null)
            {
                TrialConfigInfo = CommonUtil.TryGetJSONObject<TrialConfig>(AJRSA.Decrypt(trialCfg.StringValue.Base64Decrypt(), string.Empty, AJRSA.KeyFormat.PKCS8)) ?? new TrialConfig();

                if (DateTime.Now < TrialConfigInfo.ExpireDate)
                {
                    IsAuthorized = true;
                    return ProcessResult.DefaultSuccess;
                }
            }

            var cfg = cfgs.FirstOrDefault(p => p.Key == Core.Enums.SystemConfigKey.LicenseConfig);

            var activeCodes = new Dictionary<string, string>();

            var newCfg = false;
            var updated = false;
            if (cfg == null)
            {
                newCfg = true;
                cfg = new Core.Models.EF.Tables.SystemConfigDictionary
                {
                    Key = Core.Enums.SystemConfigKey.LicenseConfig,
                    StringValue = string.Empty,
                    CreateDate = DateTime.Now,
                };
                db.SystemConfigDictionaries.Add(cfg);
                updated = true;
            }
            else
            {
                activeCodes = CommonUtil.TryGetJSONObject<Dictionary<string, string>>(cfg.StringValue) ?? new Dictionary<string, string>();
            }

            if (string.IsNullOrWhiteSpace(registerCode))
            {
                if (activeCodes.ContainsKey(_machineMd5))
                {
                    var regCode = activeCodes[_machineMd5];
                    if (!string.IsNullOrWhiteSpace(regCode))
                    {
                        registerCode = regCode.Base64Decrypt();
                    }
                }
            }

            var result = await ActiveAsync(registerCode, autoMode);
            if (!result.Success)
            {
                if (tryLocally)
                {
                    // 如果是激活码不存在,机器码不对,已过期,则清空本地激活码配置
                    if (result.Message == "不正确的激活码:402"
                        || result.Message == "激活码已到期")
                    {
                        if (!newCfg)
                        {
                            cfg.StringValue = string.Empty;
                            db.Entry(cfg).State = EntityState.Modified;
                            updated = true;
                        }
                    }

                    // 使用本地激活
                    result = ActiveLocally(registerCode);
                }
            }
            else
            {
                // 移除试用数据
                if (trialCfg != null)
                {
                    db.SystemConfigDictionaries.Remove(trialCfg);
                    updated = true;
                }
                // 在线激活成功, 则覆盖本地配置
                var json = CommonUtil.AJSerializeObject(result.Data);
                var codeData = CommonUtil.TryGetJSONObject<ActiveCode>(json);

                if (activeCodes.ContainsKey(_machineMd5))
                {
                    activeCodes[_machineMd5] = codeData.RegisterCode.Base64Encrypt();
                }
                else
                {
                    activeCodes.Add(_machineMd5, codeData.RegisterCode.Base64Encrypt());
                }

                cfg.StringValue = CommonUtil.AJSerializeObject(activeCodes);
                if (!newCfg)
                {
                    db.Entry(cfg).State = EntityState.Modified;
                    updated = true;
                }

                var array = codeData.RegisterCode.Split('\n');

                var key1 = array.ElementAtOrDefault(0);

                var key1Array = key1.Base64Decrypt().Split('-');

                PID = key1Array.ElementAtOrDefault(1).TryGetInt();

                result.SetSuccess(codeData);
            }

            if (updated)
            {
                await db.SaveChangesAsync();
            }
            IsAuthorized = result.Success;
            return result;
        }


        /// <summary>
        /// 在线激活
        /// </summary>
        /// <param name="registerCode"></param>
        /// <returns></returns>
        public async Task<ProcessResult> ActiveAsync(string registerCode, bool autoMode)
        {
            // 如果没有注册码, 则随机赋值, 提交到接口,  接口会负责使用机器码去匹配激活码 --阿吉 2024年3月14日18点54分
            if (string.IsNullOrWhiteSpace(registerCode))
            {
                registerCode = Guid.NewGuid().ToString();
            }
            var result = await PostAsync("/system/activecode/active", new JObject
            {
                {"hardwareCode",MACHINEMD5 },
                {nameof(registerCode),registerCode },
            });
            if (!autoMode)
            {
                return result;
            }
            if (!result.Success && result.Message == "激活码已使用")
            {
                result.Success = true;
            }
            return result;
        }

        public ProcessResult ActiveLocally(string registerCode)
        {
            var result = new ProcessResult();

            if (string.IsNullOrWhiteSpace(registerCode))
            {
                result.SetError("激活码为空");
                return result;
            }

            var array = registerCode.Split('\n');

            if (array.Length != 3)
            {
                result.SetError("不正确的激活码:400");
                return result;
            }

            var key1 = array.ElementAtOrDefault(0);
            if (string.IsNullOrWhiteSpace(key1))
            {
                result.SetError("不正确的激活码:301");
                return result;
            }
            var key2 = array.ElementAtOrDefault(2);
            if (string.IsNullOrWhiteSpace(key2))
            {
                result.SetError("不正确的激活码:302");
                return result;
            }

            var key1Array = key1.Base64Decrypt().Split('-');

            key1 = key1Array.FirstOrDefault();
            key2 = key2.Base64Decrypt().Split('-').FirstOrDefault();

            PID = key1Array.ElementAtOrDefault(1).TryGetInt();

            var middle = (array.ElementAtOrDefault(1) ?? string.Empty).AESDecrypt($"{key1}{key2}");

            var codeData = CommonUtil.TryGetJSONObject<ActiveCode>(middle);

            if (codeData == null)
            {
                result.SetError("不正确的激活码:401");
                return result;
            }

            if (codeData.HardwareCode != MACHINEMD5)
            {
                result.SetError("激活码数据错误:400");
                return result;
            }

            if (DateTime.Now > codeData.ExpireDate)
            {
                result.SetError("激活码已到期");
                return result;
            }

            result.SetSuccess(codeData);

            return result;
        }

        private async Task<ProcessResult> PostAsync(string path, JObject body = null, JObject query = null)
        {
            var result = new ProcessResult();

            try
            {
                body ??= new JObject();
                var queryStr = string.Empty;
                if (query != null)
                {
                    queryStr = $"?{string.Join("&", query.Properties().Select(p => $"{p.Name}={p.Value}"))}";
                }
                var content = new StringContent(body.ToString(Formatting.None), Encoding.UTF8, "application/json");

                var res = await _client.PostAsync($"{APIDOMAIN}{path}{queryStr}", content);

                if (res.StatusCode != System.Net.HttpStatusCode.OK)
                {
                    throw new HttpRequestException(res.StatusCode.ToString());
                }

                var json = await res.Content.ReadAsStringAsync();

                return CommonUtil.TryGetJSONObject<ProcessResult>(json);
            }
            catch (Exception e)
            {
                result.SetError(e.Message);
            }

            return result;

        }

        #region Models

        /// <summary>
        /// 平台激活码模型
        /// </summary>
        public class ActiveCode
        {
            public string RegisterCode { get; set; }
            public string HardwareCode { get; set; }
            public DateTime ExpireDate { get; set; }
            public string StationName { get; set; }
            public string CompanyName { get; set; }
            public bool EnableCustomPage { get; set; }
            public bool EnableRemoteDataView { get; set; }
            public bool EnableRemotePictureView { get; set; }
            public bool EnableReserveWeigh { get; set; }
            public string OtherParameters { get; set; }
        }

        public class TrialConfig
        {
            public DateTime ExpireDate { get; set; }
        }

        public void Dispose()
        {
            _client?.Dispose();
        }

        #endregion
    }
}
